<?php

namespace App\Http\Controllers\Api;


use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\Constraint\IdentifiedBy;
use Lcobucci\JWT\Validation\Constraint\IssuedBy;

class Token
{

    private $config;
    private static $instance;// 单例模式JwtAuth句柄


    public static function getInstance(){
        if(is_null(self::$instance)){
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function __construct()
    {
        self::init();
    }

    private function init()
    {
        $config = Configuration::forSymmetricSigner(
            new Sha256(),
            InMemory::plainText(env('APP_KEY'))
        );
        $this->config = $config;
    }
    //生成token
    public function createToken($userId)
    {
       $config = $this->config;
       assert($config instanceof Configuration);
        $timezone = new \DateTimeZone('Asia/Shanghai');
        $now = new \DateTimeImmutable('now',$timezone);
       $token = $config->builder()
           // 配置颁发者（iss声明）
           ->issuedBy('laravel.com')
           // 配置访问群体（aud声明）
//           ->permittedFor('laravel.com')
           // 配置id（jti声明）
           ->identifiedBy('1')
           // 配置一个名为“uid”的新声明
           ->withClaim('uid', $userId)
           // 配置令牌发出的时间（iat声明）
           ->issuedAt($now)
           // 配置令牌的过期时间（exp claim）
           ->expiresAt($now->modify("+24 hour"))
           // 生成新令牌
           ->getToken($config->signer(), $config->signingKey());
        return $token->toString();

    }

    //从请求信息中获取token令牌
    public function getUserWithToken($jwt)
    {
        $config = $this->config;
        assert($config instanceof Configuration);
        $token = $config->parser()->parse($jwt);
        assert($token instanceof UnencryptedToken);
        return $token->claims()->get('uid');
    }

    //从token中获取用户id （包含token的校验）
    public function validatorToken($jwt)
    {
        $timezone = new \DateTimeZone('Asia/Shanghai');
        $now = new \DateTimeImmutable('now',$timezone);
       try{
           $config = $this->config;
           assert($config instanceof Configuration);
           $token = $config->parser()->parse($jwt);
           assert($token instanceof UnencryptedToken);
       }catch (\RuntimeException $exception){
           throw new \RuntimeException(json_encode(array('msg'=>'非法token')));
       }
        $claims = $token->claims();
        $jti = (string)$claims->get('jti');
        $iss = (string)$claims->get('iss');
        $exp = $claims->get('exp');
        $exp =\date("Y-m-d H:i:s",strtotime($exp->format("Y-m-d H:i:s"))+8*60*60);
        $exp = new \DateTimeImmutable($exp,$timezone);
        if($exp < $now){
            throw new \RuntimeException(json_encode(array('msg'=>'token已失效')));
        }
        //验证jwt id是否匹配
        $validate_jwt_id = new IdentifiedBy($jti);
        // 验证签发人url是否正确
        $validate_issued = new IssuedBy($iss);
        // 验证客户端url是否匹配
//        $validate_aud = new PermittedFor($aud[0]);

//        需要注意这里！传入对应的验证数据
        $config->setValidationConstraints($validate_jwt_id, $validate_issued);

        $constraints = $config->validationConstraints();

//        验证方法1
//        try {
//            $config->validator()->assert($token, ...$constraints);
//        } catch (RequiredConstraintsViolated $e) {
//            // list of constraints violation exceptions:
//            var_dump($e->violations());
//        }
//        验证方法2
        if (!$config->validator()->validate($token, ...$constraints)) {
            throw new \RuntimeException(json_encode(['msg'=>'No way!']));
        }
//        return  json_encode(array('data'=>'','msg'=>'验证通过','code'=>1,'state'=>1));
    }

}
